`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: CBICR, Tsinghua Univ.
// Engineer: Hongyi Li
// 
// Create Date: 2024/12/23 14:59:00
// Design Name: 
// Module Name: Synchronous FIFO
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module SyncFifo #(
	parameter width = 'd32,
    parameter depth = 'd4
)(
    input  wire                      clk, rst_n,
    input  wire                      i_push_req,
    input  wire                      i_pop_req,
    input  wire [width-1: 0]         i_data,
    output wire                      o_empty,
    output wire [$clog2(depth)-1:0]  o_credits,
    output wire [width-1: 0]         o_data
);

// regs to use (1 + width * depth + 3 * $clog2(depth))
reg                      empty;
reg [width-1:0]          mem [0:depth-1]; 
reg [$clog2(depth)-1:0]  write_ptr;
reg [$clog2(depth)-1:0]  read_ptr;
reg [$clog2(depth)-1:0]  fill_cnt;

// Comb-Logic
assign o_credits = fill_cnt;
assign o_data = mem[read_ptr];
assign o_empty = empty;

// Seq-Logic: Write Pointer
always @(posedge clk) begin
    if (~rst_n) begin
        write_ptr <= 2'b0;
    end else if (i_push_req)
        write_ptr <= write_ptr + {{($clog2(depth)-1){1'b0}},1'b1};
    else
         write_ptr <= write_ptr;
end

// Seq-Logic: Read Pointer
always @(posedge clk) begin
    if (!rst_n)
        read_ptr <= 2'b0;
    else if (i_pop_req)
        read_ptr <= read_ptr + {{($clog2(depth)-1){1'b0}},1'b1};
    else
        read_ptr <= read_ptr;
end

// Seq-Logic: Read Pointer
always @(posedge clk) begin
    if (!rst_n)
        fill_cnt <= 2'b0;
    else if (i_push_req && !i_pop_req && !empty)
        fill_cnt <= fill_cnt + {{($clog2(depth)-1){1'b0}}, 1'b1};
    else if (i_push_req && i_pop_req)
        fill_cnt <= fill_cnt;
    else if (i_pop_req && |fill_cnt)
        fill_cnt <= fill_cnt - {{($clog2(depth)-1){1'b0}}, 1'b1};
    else
        fill_cnt <= fill_cnt;
end

// Seq-Logic: Empty Signal
always @(posedge clk) begin
    if (!rst_n)
        empty <= 1'b1;
    else if (i_push_req)
        empty <= 1'b0;
    else if (i_pop_req)
        empty <= ~|fill_cnt; 
    else
        empty <= empty;
end

// Seq-Logic: Memory
genvar i;
generate
    for (i = 0; i < depth; i = i + 1) begin
        always @(posedge clk) begin
            if (!rst_n) 
                mem[i] <= 0;
            else if (i_push_req && (write_ptr == i))  
                mem[i] <= i_data;
            else                                  
                mem[i] <= mem[i];
        end
    end
endgenerate
    
endmodule 